Appearance
Submitted 2026-09-13 — all 2 sample case(s) passed.
cpp
// P1792 [国家集训队] 种树 —— 费用流(最小费用最大流)版本
//
// 建模:
// 位置 i 种树 = 一条"匹配边"。把环上相邻的两个位置看作一条边,
// 于是"选 m 个互不相邻的位置"= 在路径/环图上选 m 条两两不共端点的边,
// 也就是大小为 m 的(最大权)匹配。匹配用费用流求:
// S -> L_v 容量 1,费用 0 (顶点 v 最多被用一次)
// L_v -> R_u 容量 1,费用 -A_i (图上的边 {v,u},即种位置 i)
// R_u -> T 容量 1,费用 0
// 顶点按下标的奇偶二染色(路径是二分图),所以只连 偶->奇 方向。
// 推 m 单位流量、费用取负即为答案。
//
// 环的处理:枚举 1 号位置种不种,各退化成一个路径上的匹配问题:
// ① 1 号不种:位置 2..n 上选 m 个;
// ② 1 号种: 必须先选 A_1,位置 3..n-1 上再选 m-1 个(2 和 n 被禁)。
// 取两种情形的较大值。
//
// 复杂度 O(m · E log V),E = O(n):正确但只有中小数据跑得动。
// 上限数据 (n=2e5, m=1e5) 请用 sol.cpp —— 堆+双向链表那版正是这份费用流
// 的"模拟费用流"写法(每次增广恰好 +1 棵树,退流边就是反悔项)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = (ll)4e18;
const ll NEG = -(ll)1e18;
struct MCMF {
struct Edge {
int to, rev, cap;
ll cost;
};
vector<vector<Edge>> g;
explicit MCMF(int n) : g(n) {}
void add(int u, int v, int cap, ll cost) {
g[u].push_back({v, (int)g[v].size(), cap, cost});
g[v].push_back({u, (int)g[u].size() - 1, 0, -cost});
}
/** min cost to push exactly `need` units; INF if impossible */
ll flow(int s, int t, int need) {
if (need == 0) return 0;
const int n = (int)g.size();
vector<ll> h(n, INF), dist(n);
vector<int> prevv(n), preve(n);
// initial potentials: SPFA (the graph has negative cost arcs)
deque<int> dq;
vector<bool> inq(n, false);
h[s] = 0;
dq.push_back(s);
inq[s] = true;
while (!dq.empty()) {
int u = dq.front();
dq.pop_front();
inq[u] = false;
for (const Edge &e : g[u]) {
if (e.cap > 0 && h[u] + e.cost < h[e.to]) {
h[e.to] = h[u] + e.cost;
if (!inq[e.to]) {
inq[e.to] = true;
dq.push_back(e.to);
}
}
}
}
ll total = 0;
while (need > 0) {
// Dijkstra on the reduced costs
fill(dist.begin(), dist.end(), INF);
dist[s] = 0;
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top();
pq.pop();
if (d > dist[u]) continue;
for (int i = 0; i < (int)g[u].size(); ++i) {
const Edge &e = g[u][i];
if (e.cap <= 0) continue;
ll nd = d + e.cost + h[u] - h[e.to];
if (nd < dist[e.to]) {
dist[e.to] = nd;
prevv[e.to] = u;
preve[e.to] = i;
pq.push({nd, e.to});
}
}
}
if (dist[t] == INF) return INF;
for (int v = 0; v < n; ++v)
if (dist[v] < INF) h[v] += dist[v];
int push = need;
for (int v = t; v != s; v = prevv[v])
push = min(push, g[prevv[v]][preve[v]].cap);
for (int v = t; v != s; v = prevv[v]) {
Edge &e = g[prevv[v]][preve[v]];
e.cap -= push;
g[v][e.rev].cap += push;
}
total += (ll)push * h[t]; // h[t] = true shortest distance s->t
need -= push;
}
return total;
}
};
/** best total of exactly `need` pairwise non-adjacent values from a LINE of
* values; NEG when there are not enough positions */
ll pathBest(const vector<ll> &w, int need) {
const int k = (int)w.size();
if (need == 0) return 0;
if (need > (k + 1) / 2) return NEG; // ceiling(k/2) is the maximum
// gap vertices 0..k, item j = edge (j, j+1); colour vertices by parity
const int S = 0, T = 1;
const int L = 2; // L_v = L + v
const int R = L + (k + 1); // R_v = R + v
MCMF f(R + (k + 1));
for (int v = 0; v <= k; ++v) {
if (v % 2 == 0) f.add(S, L + v, 1, 0);
else f.add(R + v, T, 1, 0);
}
for (int j = 0; j < k; ++j) { // item j joins vertices j and j+1
if (j % 2 == 0) f.add(L + j, R + (j + 1), 1, -w[j]);
else f.add(L + (j + 1), R + j, 1, -w[j]);
}
ll cost = f.flow(S, T, need);
return cost >= INF ? NEG : -cost;
}
int main() {
int n, m;
if (scanf("%d %d", &n, &m) != 2) return 0;
vector<ll> a(n + 1);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
if (m > n / 2) { // at most floor(n/2) trees fit on the ring
printf("Error!\n");
return 0;
}
if (m == 0) {
printf("0\n");
return 0;
}
ll best = NEG;
{ // case 1: position 1 is not planted -> positions 2..n form a line
vector<ll> line;
for (int i = 2; i <= n; ++i) line.push_back(a[i]);
ll v = pathBest(line, m);
if (v > NEG / 2) best = max(best, v);
}
{ // case 2: position 1 is planted -> pick m-1 from 3..n-1
vector<ll> line;
for (int i = 3; i <= n - 1; ++i) line.push_back(a[i]);
ll v = pathBest(line, m - 1);
if (v > NEG / 2) best = max(best, a[1] + v);
}
printf("%lld\n", best);
return 0;
}